Nginx Master 进程主流程
nginx master 进程
之前有说到 nginx 进程模型-整体架构,下面来看一下 nginx master
进程的主要工作
nginx
的入口 main
函数在 nginx.c
文件中
函数原型为:
int ngx_cdecl
main(int argc, char *const *argv)
在这个函数中,master
做了一系列的初始化操作
最终在下面这个地方进入了主流程中:
//...
if (ngx_process == NGX_PROCESS_SINGLE) {
ngx_single_process_cycle(cycle);
} else {
ngx_master_process_cycle(cycle);
}
//...
因为我们主要看 master-worker
这种进程模型,所以进入 ngx_master_process_cycle
设置信号屏蔽字,防止创建子进程过程中被信号中断
// 先清空信号集
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
sigaddset(&set, SIGALRM);
sigaddset(&set, SIGIO);
sigaddset(&set, SIGINT);
sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL));
// 设置信号屏蔽字,将 set 中的信号设置为阻塞状态,防止创建worker 的过程中,被进来的信号打断
if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"sigprocmask() failed");
}
// 将 set 清空
sigemptyset(&set);
设置信号屏蔽字,防止在创建子进程的过程中被信号处理程序中断
关于信号屏蔽字,引用 《UNIX 环境高级编程》
中信号一节的部分内容:
进程可以选用
“阻塞信号递送”
。如果为进程产生了一个阻塞
的信号,而且对该信号的动作是系统默认动作或捕捉该信号, 则为该进程将此信号保持为未决状态
,直到该进程对此信号解除了阻塞, 或者将对此信号的动作更改为忽略。内核在递送
一个原来被阻塞的信号给进程时(而不是在产生该信号时),才决定对它的处理方式。于是进程在信号递送给它之前仍可改变对该信号的动作。进程调用sigpending
函数(见10.13节)来判定哪些信号是设置为阻塞并处于未决状态的。
每个进程都有一个
信号屏蔽字( signal mask)
,它规定了当前要阻塞递送到该进程的信号集
。对于每种可能的信号,该屏蔽字中都有一位与之对应。对于某种信号,若其对应位已设置,则它当前是被阻塞的。进程可以调用sigprocmask
(在10.12节中说明)来检测和更改其当前信号屏蔽字。
当然,在下面创建完子进程之后,会使用 sigsuspend
解除信号屏蔽,并使 master 进程进入休眠
关于 sigsuspend
函数,简单来说,它是一个 sigprocmask(SIG_SETMASK, &emptyset, NULL)
和 pause()
函数的结合体,不过相对于使用两个函数完成上述操作,sigsuspend
是 原子操作
。
具体的操作如下:
- 使用新的信号集合设置屏蔽字,在这里是清空屏蔽字
- 调用信号处理函数,并从信号处理程序返回
- 屏蔽字恢复成调用
sigsuspend
之前的值(再次不让进程被信号打断)
设置 master 进程的 title
static u_char master_process[] = "master process";
size = sizeof(master_process);
for (i = 0; i < ngx_argc; i++) {
size += ngx_strlen(ngx_argv[i]) + 1;
}
title = ngx_pnalloc(cycle->pool, size);
if (title == NULL) {
/* fatal */
exit(2);
}
p = ngx_cpymem(title, master_process, sizeof(master_process) - 1);
for (i = 0; i < ngx_argc; i++) {
*p++ = ' ';
p = ngx_cpystrn(p, (u_char *) ngx_argv[i], size);
}
ngx_setproctitle(title);
将进程title
设置成三个部分:
- 固定字符:
nginx:
- 主进程标志:
master process
- 命令行启动的命令,如:
/home/ubuntu/mydisk/var/nginx-debug-1/sbin/nginx
所以进程刚开始是这样显示的:
$ ps aux | grep nginx | grep -v grep
root 183117 0.0 0.0 4324 2680 ? ts 17:07 0:00 /home/ubuntu/mydisk/var/nginx-debug-1/sbin/nginx
设置了 title 之后,变成了下面这样:
$ ps aux | grep nginx | grep -v grep
root 183117 0.0 0.0 4324 2680 ? ts 17:07 0:00 nginx: master process /home/ubuntu/mydisk/var/nginx-debug-1/sbin/nginx
根据配置启动相应数量的 worker
和 cache
管理进程
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
ngx_start_worker_processes(cycle, ccf->worker_processes,
NGX_PROCESS_RESPAWN);
ngx_start_cache_manager_processes(cycle, 0);
监听信号,并作出响应
在 ngx_init_signals
函数中,对原始信号做了变量名映射,具体映射如下:
信号 | 对应进程中的标志位变量 | 含义 |
---|---|---|
QUIT | ngx_quit | 优雅关闭服务 |
TERM 或 INT | ngx_terminate | 强制关闭服务 |
USR1 | ngx_reopen | 重新打开服务中的所有文件 |
WINCH | ngx_noaccept | 所有子进程不再接受处理新的连接,实际相当于对所有的子进程发送 QUIT 信号 |
USR2 | ngx_change_binary | 平滑升级到新版本的 Nginx 程序 |
HUP | ngx_reconfigure | 重新读取配置文件并使服务对新配置项生效 |
CHLD | ngx_reap | 有子进程意外结束 master 会监控所有子进程,并在子进程意外退出时调用 ngx_reap_children 方法重启子进程 |
master 并不是时刻不停的执行循环检测这些标志位,而是通过 sigsuspend
进入休眠,等待有信号唤醒进程时,再循环检测所有信号并处理。
参考
《UNIX 环境高级编程》
本站总访问量次 本站访客数人次 本文总阅读量次